package com.eksframework.commons.core.base;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Optional;

/**
 ** 通用数字操作类
 *
 * @author: <a href="mailto:chenfenghai@oristartech.com">chenfenghai</a>
 * @version: 1.0
 * @date: 2019-10-21 14:18
 */
public class EksNumber {

    /** The number of BigDecimal */
    private BigDecimal bigDecimal;

    /**
     ** Constructors
     */
    EksNumber(){
        this.bigDecimal = BigDecimal.ZERO;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 私有的带参数构造方法
     *
     * @param bigDecimal 传入数字值
     */
    private EksNumber(@Nonnull BigDecimal bigDecimal){
        this.bigDecimal = bigDecimal;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 初始化{@code Number}实例，如果为null，则默认为0
     *
     * @param number 传入数字值
     * @return {@code Number}, not null
     */
    @Nonnull
    public static EksNumber of(@Nullable final BigDecimal number){
        return number == null ? new EksNumber() : new EksNumber(number);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 初始化{@code Number}实例，如果传入为null或不是数字格式，则默认为0
     *
     * @param number 传入数字值
     * @return {@code Number}, not null
     */
    @Nonnull
    public static EksNumber of(@Nullable final Object number){
        return number == null ? new EksNumber() : new EksNumber(EksConverter.of(number).toBigDecimal());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 初始化Number实例
     *
     * @param number 传入数字值
     * @param defaultNumber 当转换出错时默认数字值
     * @return {@code Number}, not null
     * @throws NullPointerException if {@code defaultNumber} is {@code null}
     */
    @Nonnull
    public static EksNumber of(@Nullable final Object number, @Nonnull final Object defaultNumber){
        if(number == null){
            return of(defaultNumber);
        }
        try{
            return of(number);
        }catch (Exception ex){
            return of(defaultNumber);
        }
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     * 加上一个数值
     *
     * @param  augend value to be added to this {@code Number}.
     * @return {@code Number}, not null
     */
    @Nonnull
    public EksNumber add(@Nullable final Object augend) {
        return addDefault(augend, 0);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     * 加上一个数值
     *
     * @param  augend value to be added to this {@code Number}.
     * @return {@code Number}, not null
     * @throws NullPointerException if {@code defaultNumber} is {@code null}
     */
    @Nonnull
    public EksNumber addDefault(@Nullable final Object augend, @Nonnull final Object defaultNumber) {
        bigDecimal = bigDecimal.add(EksConverter.of(augend).toBigDecimalDefault(defaultNumber));
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 减去一个数值
     *
     * @param  subtrahend value to be subtracted from this {@code Number}.
     * @return {@code Number}, not null
     */
    @Nonnull
    public EksNumber subtract(@Nullable final Object subtrahend) {
        return subtractDefault(subtrahend, 0);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 减去一个数值
     *
     * @param  subtrahend value to be subtracted from this {@code Number}.
     * @return {@code Number}, not null
     * @throws NullPointerException if {@code defaultNumber} is {@code null}
     */
    @Nonnull
    public EksNumber subtractDefault(@Nullable final Object subtrahend, @Nonnull final Object defaultNumber) {
        bigDecimal = bigDecimal.subtract(EksConverter.of(subtrahend).toBigDecimalDefault(defaultNumber));
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 乘以一个数值
     *
     * @param  multiplicand value to be multiplied by this {@code Number}.
     * @return {@code Number}, not null
     */
    @Nonnull
    public EksNumber multiply(@Nullable final Object multiplicand) {
        return multiplyDefault(multiplicand, 1);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 乘以一个数值
     *
     * @param  multiplicand value to be multiplied by this {@code Number}.
     * @return {@code Number}, not null
     * @throws NullPointerException if {@code defaultNumber} is {@code null}
     */
    @Nonnull
    public EksNumber multiplyDefault(@Nullable final Object multiplicand, @Nonnull final Object defaultNumber) {
        bigDecimal = bigDecimal.multiply(EksConverter.of(multiplicand).toBigDecimalDefault(defaultNumber));
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 除以一个数值
     *
     * @param  divisor value by which this {@code Number} is to be divided.
     * @throws ArithmeticException if the exact quotient does not have a
     *         terminating decimal expansion
     * @return {@code Number}, not null
     */
    @Nonnull
    public EksNumber divide(@Nullable final Object divisor) {
        return divideDefault(divisor, 1);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 除以一个数值
     *
     * @param  divisor value by which this {@code Number} is to be divided.
     * @throws ArithmeticException if the exact quotient does not have a
     *         terminating decimal expansion
     * @return {@code Number}, not null
     */
    @Nonnull
    public EksNumber divide(@Nullable final Object divisor, final int scale) {
        return divideDefault(divisor, scale, RoundingMode.HALF_UP, 1);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 除以一个数值
     *
     * @param  divisor value by which this {@code Number} is to be divided.
     * @throws ArithmeticException if the exact quotient does not have a
     *         terminating decimal expansion
     * @return {@code Number}, not null
     */
    @Nonnull
    public EksNumber divide(@Nullable final Object divisor, final int scale, @Nullable final RoundingMode roundingMode) {
        return divideDefault(divisor, scale, roundingMode, 1);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 除以一个数值
     *
     * @param  divisor value by which this {@code Number} is to be divided.
     * @throws ArithmeticException if the exact quotient does not have a
     *         terminating decimal expansion
     * @return {@code Number}, not null
     * @throws NullPointerException if {@code defaultNumber} is {@code null}
     */
    @Nonnull
    public EksNumber divideDefault(@Nullable final Object divisor, @Nonnull final Object defaultNumber) {
        bigDecimal = bigDecimal.divide(EksConverter.of(divisor).toBigDecimalDefault(defaultNumber), RoundingMode.HALF_UP);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 除以一个数值
     *
     * @param  divisor value by which this {@code Number} is to be divided.
     * @throws ArithmeticException if the exact quotient does not have a
     *         terminating decimal expansion
     * @return {@code Number}, not null
     * @throws NullPointerException if {@code defaultNumber} is {@code null}
     */
    @Nonnull
    public EksNumber divideDefault(@Nullable final Object divisor, final int scale,
                                   @Nullable final RoundingMode roundingMode, @Nonnull final Object defaultNumber) {
        bigDecimal = bigDecimal.divide(EksConverter.of(divisor).toBigDecimalDefault(defaultNumber), scale,
                Optional.ofNullable(roundingMode).orElse(RoundingMode.HALF_UP));
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 转换为EksConverter类
     *
     * @return {@code EksConverter}, not null
     */
    @Nonnull
    public EksConverter toEksConverter(){
        return EksConverter.of(bigDecimal);
    }

    //-----------------------------------------------------------------------------------------------------------------

}
